using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace IcyTowerMobile
{
	/// <summary>
	/// Class representing the player instance of the game.
	/// </summary>
	public class Player
	{

		// constants defining the current player move direction.
		private const int MOVE_UP = -1;
		private const int MOVE_DOWN = 1;
		private const int MOVE_LEFT = -1;
		private const int MOVE_RIGHT = 1;

		/// <summary>
		/// Amount of pixels the player should fall down per second.
		/// </summary>
		private const float GRAVITY = 50f;
		/// <summary>
		/// Constant for adjusting the player's horizontal speed.
		/// </summary>
		private const float MOVE_SPEED = 900f;
		/// <summary>
		/// Constant for adjusting the player's jump speed.
		/// </summary>
		private const float JUMP_SPEED = 700f;
		/// <summary>
		/// Threshold below that the player will perform a high rotation jump.
		/// </summary>
		private const int VELOCITY_THRESHOLD_HIGH = -29;
		/// <summary>
		/// Threshold below that the player will perform a middle jump.
		/// </summary>
		private const int VELOCITY_THRESHOLD_MID = -26;
		/// <summary>
		/// Threshold for the player standing still until a certain acceleration.
		/// </summary>
		private const float ACCELERATION_THRESHOLD_MOVEMENT = 0.25f;
		/// <summary>
		/// Rotation speed when the player performs a high rotation jump.
		/// </summary>
		private const int ROTATION_SPEED = 8;

		// player properties
		private List<Animation> animationList;
		private Vector2 playerPosition;
		private AnimationState animationState;
		private RotatedRectangle boundingBox;
		private PlayerState playerState;
		private Vector2 velocity;
		private float rotation;

		// sound effects
		private SoundEffect hello;
		private SoundEffect jumpLo;
		private SoundEffect jumpMid;
		private SoundEffect jumpHi;

		// helper variables		
		private bool saidHello;
		private Platform intersectingPlatform;

		public int IntersectThreshold
		{
			get { return (int)velocity.Y + 15; }
		}

		public Vector2 PlayerPosition
		{
			get { return playerPosition; }
		}

		public Vector2 Velocity
		{
			get { return velocity; }
		}

		public enum PlayerState
		{
			Walking,
			Jumping,
			Falling
		}

		public enum Touchstate
		{
			Touched,
			Nottouched
		}

		Vector2 direction = Vector2.Zero;

		public int Width
		{
			get { return animationList.ElementAt((int)animationState).FrameWidth; }
		}

		public int Height
		{
			get { return animationList.ElementAt((int)animationState).FrameHeight; }
		}

		/// <summary>
		/// Initializes the player object with the provided list of animations at the specified position.
		/// </summary>
		public void Initialize(List<Animation> textures, Vector2 position)
		{
			animationState = AnimationState.IDLE;
			playerPosition = position;
			animationList = textures;
			direction = Vector2.Zero;
			rotation = 0;
			playerState = PlayerState.Falling;
		}

		/// <summary>
		/// Loads player-specific content like sounds.
		/// </summary>
		/// <param name="content">Content manager to load the content from.</param>
		public void LoadContent(ContentManager content)
		{
			hello = content.Load<SoundEffect>("sounds/hello");
			jumpLo = content.Load<SoundEffect>("sounds/jump_lo");
			jumpMid = content.Load<SoundEffect>("sounds/jump_mid");
			jumpHi = content.Load<SoundEffect>("sounds/jump_hi");
		}

		/// <summary>
		/// Allows the game component to update itself.
		/// </summary>
		/// <param name="gameTime">Provides a snapshot of timing values.</param>
		public void Update(GameTime gameTime, int movement, float acceleration, int touches)
		{
			FrameworkDispatcher.Update();
			if (!saidHello && IsolatedStorageManager.GetInstance().IsSoundEnabled() && MediaPlayer.GameHasControl)
			{
				// play hello sound the first time this method gets called
				saidHello = true;
				hello.Play(1, 0, 0);
			}

			// add game movement to player position
			playerPosition.Y += movement;

			if (acceleration >= ACCELERATION_THRESHOLD_MOVEMENT)
			{
				if (animationState != AnimationState.JUMP_ROTATE)
				{
					// set animation state to run right
					animationState = AnimationState.RUN_RIGHT;
				}
				// set horizontal direction and velocity
				direction.X = MOVE_RIGHT;
				velocity.X = direction.X * MOVE_SPEED * Math.Abs(acceleration) * (float)gameTime.ElapsedGameTime.TotalSeconds;
			}
			else if (acceleration <= -ACCELERATION_THRESHOLD_MOVEMENT)
			{
				if (animationState != AnimationState.JUMP_ROTATE)
				{
					// set animation state to run left
					animationState = AnimationState.RUN_LEFT;
				}
				// set horizontal direction and velocity
				direction.X = MOVE_LEFT;
				velocity.X = direction.X * MOVE_SPEED * Math.Abs(acceleration) * (float)gameTime.ElapsedGameTime.TotalSeconds;
			}
			else
			{
				if (animationState != AnimationState.JUMP_ROTATE)
				{
					// set animation state to idle
					animationState = AnimationState.IDLE;
				}
				// set horizontal direction and velocity to zero
				direction.X = 0;
				velocity.X = 0;
			}


			if (touches > 0 && playerState == PlayerState.Walking)
			{
				// perform jump when user presses on display
				direction.Y = MOVE_UP;
				velocity.Y = -JUMP_SPEED * (Math.Abs(acceleration / 2) + 1) * (float)gameTime.ElapsedGameTime.TotalSeconds;
				playerState = PlayerState.Jumping;
				if (velocity.Y < VELOCITY_THRESHOLD_HIGH)
				{
					// perform high jump (rotation)
					if (IsolatedStorageManager.GetInstance().IsSoundEnabled() && MediaPlayer.GameHasControl)
						jumpHi.Play(1, 0, 0);
					animationState = AnimationState.JUMP_ROTATE;
				}
				else if (velocity.Y < VELOCITY_THRESHOLD_MID)
				{
					// perform mid jump
					if (IsolatedStorageManager.GetInstance().IsSoundEnabled() && MediaPlayer.GameHasControl)
						jumpMid.Play(1, 0, 0);
				}
				else
				{
					// perform low jump
					if (IsolatedStorageManager.GetInstance().IsSoundEnabled() && MediaPlayer.GameHasControl)
						jumpLo.Play(1, 0, 0);
				}
			}


			if (velocity.Y > 0)
			{
				// set vertical direction
				direction.Y = MOVE_DOWN;
			}
			else if (velocity.Y == 0 && direction.Y == 0)
			{
				// just reached a platform
				if (animationState == AnimationState.JUMP_ROTATE)
				{
					animationState = AnimationState.IDLE;
				}
			}

			if (direction.Y == MOVE_UP && animationState != AnimationState.JUMP_ROTATE)
			{
				// jump states (without rotation)
				if (direction.X == MOVE_RIGHT)
				{
					// right jump
					animationState = AnimationState.JUMP_RIGHT;
				}
				else if (direction.X == MOVE_LEFT)
				{
					// left jump
					animationState = AnimationState.JUMP_LEFT;
				}
				else
				{
					// straight jump
					animationState = AnimationState.JUMP_STRAIGHT;
				}
			}

			if (animationState == AnimationState.JUMP_ROTATE)
			{
				// compute rotation according to ellapsed time and rotation speed
				rotation += ((float)gameTime.ElapsedGameTime.TotalSeconds * ROTATION_SPEED) % (float)Math.PI;
			}
			else
			{
				// no rotation in all other animations
				rotation = 0;
			}

			// apply gravity on player
			velocity.Y += GRAVITY * (float)gameTime.ElapsedGameTime.TotalSeconds;

			if (playerState != PlayerState.Walking)
			{
				// add vertical velocity (only if player is currently not walking on a platform)
				playerPosition.Y += velocity.Y;
			}
			else if (intersectingPlatform != null && intersectingPlatform.PlayerIsHighEnough)
			{
				// add platform speed if player is currently walking on a platform
				playerPosition.Y += intersectingPlatform.Speed;
			}

			// add horizontal velocity
			playerPosition.X += velocity.X;

			// recompute bounding box
			boundingBox = new RotatedRectangle(new Rectangle((int)playerPosition.X, (int)playerPosition.Y, Width, Height), rotation);
		}


		public void Draw(SpriteBatch spriteBatch)
		{
			animationList.ElementAt((int)animationState).Draw(spriteBatch, rotation);
		}


		public void UpdateAnimation(GameTime gameTime)
		{
			animationList.ElementAt((int)animationState).Update(gameTime, boundingBox.CollisionRectangle);
		}

		/// <summary>
		/// Checks if the player intersects with a background brick and performs the necessary player repositioning.
		/// </summary>
		/// <param name="brick">The brick which should be checked.</param>
		public void checkBrickIntersect(BackgroundBrick brick)
		{
			if (boundingBox.Intersects(brick.BoundingBox))
			{
				if (brick.IsLeft)
				{
					float minX = Math.Min(Math.Min(boundingBox.UpperRightCorner().X, boundingBox.UpperLeftCorner().X), Math.Min(boundingBox.LowerRightCorner().X, boundingBox.LowerLeftCorner().X));
					playerPosition.X += brick.BoundingBox.Right - minX;
				}
				else
				{
					float maxX = Math.Max(Math.Max(boundingBox.UpperRightCorner().X, boundingBox.UpperLeftCorner().X), Math.Max(boundingBox.LowerRightCorner().X, boundingBox.LowerLeftCorner().X));
					playerPosition.X -= maxX - brick.BoundingBox.Left;
				}
				// recompute bounding box
				boundingBox = new RotatedRectangle(new Rectangle((int)playerPosition.X, (int)playerPosition.Y, Width, Height), rotation);
			}
		}

		/// <summary>
		/// Checks if the player intersects with any platform and performs the necessary player repositioning.
		/// </summary>
		/// <param name="brick">The platforms which should be checked.</param>
		public Platform checkPlatformIntersect(List<Platform> platforms)
		{
			int biggestYPosition = -1;
			Platform temp = null;

			foreach (Platform p in platforms)
			{
				if (boundingBox.Intersects(p.BoundingBox))
				{
					if ((playerPosition.Y + Height) <= (p.Position.Y + IntersectThreshold))
					{
						if (direction.Y == MOVE_DOWN)
						{
							if (p.Position.Y > biggestYPosition)
							{
								// intersection with current platform detected
								biggestYPosition = (int)p.BoundingBox.Top;
								temp = p;
								intersectingPlatform = p;
							}
						}
					}
				}
			}

			if (biggestYPosition > -1)
			{
				// at least one intersection was detected
				playerPosition.Y = biggestYPosition - Height;
				playerState = PlayerState.Walking;
				direction.Y = 0;
				velocity.Y = 0;
				// recompute bounding box
				boundingBox = new RotatedRectangle(new Rectangle((int)playerPosition.X, (int)playerPosition.Y, Width, Height), rotation);
			}
			else
			{
				// no intersection
				playerState = PlayerState.Falling;
			}
			return temp;
		}
	}
}
